﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenTK.Graphics.OpenGL;
using OpenTK.Mathematics;

namespace TiverSharp.Service
{
    public class Mesh
    {
        private int _vertexBufferObject;        //顶点缓存句柄   VBO
        private int _vertexArrayObject;       //顶点列表句柄   VAO句柄
        private int _elementBufferObject;      //顶点元素句柄   EAO句柄

        private Shader _shader;
        private TextureLoader _texture;
        public Vertex[] _vertices;
        public uint[] _indices;
        public Texture[] _textures;

        int vertexSize = 8;

        public Mesh(Shader shader, float[] vertices, uint[] indices)
        {
            /*
           
           */
            //==========================================
            //float[] vvs =
            //{
            //     0.5f,  0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,  // top right
            //     0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,  // bottom right
            //    -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,  // bottom left
            //    -0.5f,  0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,  // top left
            //};
            //vertices = vvs;
            //uint[] ids =
            //   {
            //        // Note that indices start at 0!
            //        0, 1, 3, // The first triangle will be the bottom-right half of the triangle
            //        1, 2, 3  // Then the second will be the top-right half of the triangle
            //    };
            //indices = ids;
            //==========================================

            //unsafe { vertexSize = sizeof(Vertex); }
            this._vertices = new Vertex[vertices.Length / vertexSize];
            for (int i = 0; i < vertices.Length / vertexSize; i++)
            {
                Vertex vertex = new Vertex();
                vertex.Position = new Vector3(vertices[i * vertexSize], vertices[i * vertexSize + 1], vertices[i * vertexSize + 2]);
                vertex.Normal = new Vector3(vertices[i * vertexSize + 3], vertices[i * vertexSize + 4], vertices[i * vertexSize + 5]);
                vertex.TexCoords = new Vector2(vertices[i * vertexSize + 6], vertices[i * vertexSize + 7]);
                this._vertices[i] = vertex;//.Append();
            }
            this._indices = indices;
            this._textures = new Texture[0];
            this._shader = shader;

            SetupMesh();
        }

        public Mesh(float[] vertices, uint[] indices)
        {
            /*
           
           */
            //==========================================
            //float[] vvs =
            //{
            //     0.5f,  0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,  // top right
            //     0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,  // bottom right
            //    -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,  // bottom left
            //    -0.5f,  0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,  // top left
            //};
            //vertices = vvs;
            //uint[] ids =
            //   {
            //        // Note that indices start at 0!
            //        0, 1, 3, // The first triangle will be the bottom-right half of the triangle
            //        1, 2, 3  // Then the second will be the top-right half of the triangle
            //    };
            //indices = ids;
            //==========================================

            //unsafe { vertexSize = sizeof(Vertex); }
            this._vertices = new Vertex[vertices.Length / vertexSize];
            for (int i = 0; i < vertices.Length / vertexSize; i++)
            {
                Vertex vertex = new Vertex();
                vertex.Position = new Vector3(vertices[i * vertexSize], vertices[i * vertexSize + 1], vertices[i * vertexSize + 2]);
                vertex.Normal = new Vector3(vertices[i * vertexSize + 3], vertices[i * vertexSize + 4], vertices[i * vertexSize + 5]);
                vertex.TexCoords = new Vector2(vertices[i * vertexSize + 6], vertices[i * vertexSize + 7]);
                this._vertices[i] = vertex;//.Append();
            }
            this._indices = indices;
            this._textures = new Texture[0];

            SetupMesh();
        }

        public Mesh(Shader shader, Vertex[] vertices, uint[] indices, Texture[] textures)
        {
            //unsafe { vertexSize = sizeof(Vertex); }
            this._vertices = vertices;
            this._indices = indices;
            this._textures = textures;
            this._shader = shader;

            SetupMesh();
        }

        ~Mesh()
        {
            GL.BindVertexArray(0);
            GL.DeleteVertexArray(this._vertexArrayObject);

            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
            GL.DeleteBuffer(this._vertexBufferObject);

            GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
            GL.DeleteBuffer(this._elementBufferObject);
        }



        private void SetupMesh()
        {
            this._vertexBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);
            GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * vertexSize * sizeof(float), _vertices, BufferUsageHint.StaticDraw);

            this._vertexArrayObject = GL.GenVertexArray();
            GL.BindVertexArray(this._vertexArrayObject);
            int vertextLoc = this._shader.GetAttribLocation("aPosition");    //获得shader内变量的layout句柄位置
            GL.VertexAttribPointer(vertextLoc, 3, VertexAttribPointerType.Float, false, vertexSize * sizeof(float), 0);
            int colorLoc = this._shader.GetAttribLocation("aColor");
            GL.VertexAttribPointer(colorLoc, 3, VertexAttribPointerType.Float, false, vertexSize * sizeof(float), 3 * sizeof(float));
            int texLoc = this._shader.GetAttribLocation("aTexCoord");
            GL.EnableVertexAttribArray(texLoc);         //  组号2 
            GL.VertexAttribPointer(texLoc, 2, VertexAttribPointerType.Float, false, vertexSize * sizeof(float), 6 * sizeof(float));
            //GL.EnableVertexAttribArray(0);
            GL.EnableVertexAttribArray(vertextLoc);     //上述定义的防止顶点的缓存组  组号0
            GL.EnableVertexAttribArray(colorLoc);       //上述定义的防止顶点的缓存组  组号1 

            // We create/bind the Element Buffer Object EBO the same way as the VBO, except there is a major difference here which can be REALLY confusing.
            // The binding spot for ElementArrayBuffer is not actually a global binding spot like ArrayBuffer is. 
            // Instead it's actually a property of the currently bound VertexArrayObject, and binding an EBO with no VAO is undefined behaviour.
            // This also means that if you bind another VAO, the current ElementArrayBuffer is going to change with it.
            // Another sneaky part is that you don't need to unbind the buffer in ElementArrayBuffer as unbinding the VAO is going to do this,
            // and unbinding the EBO will remove it from the VAO instead of unbinding it like you would for VBOs or VAOs.
            this._elementBufferObject = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, _elementBufferObject);
            // We also upload data to the EBO the same way as we did with VBOs.
            GL.BufferData(BufferTarget.ElementArrayBuffer, _indices.Length * sizeof(uint), _indices, BufferUsageHint.StaticDraw);
            // The EBO has now been properly setup. Go to the Render function to see how we draw our rectangle now!

            _texture = new TextureLoader(0);
            _texture = _texture.LoadFromFile("./Resource/Texture/wood.jpg");
            _texture.Use(TextureUnit.Texture0);

        }


        public void Draw()
        {
            _texture.Use(TextureUnit.Texture0);

            //this._shader = shader;
            this._shader.Use();

            foreach (Texture texture in _textures)
            {
                if (texture.type == "texture_diffuse")
                {
                    GL.ActiveTexture(TextureUnit.Texture0);
                    GL.BindTexture(TextureTarget.Texture2D, texture.id);
                    _shader.SetInt("material.diffuse", 0);
                }
                else if (texture.type == "texture_specular")
                {
                    GL.ActiveTexture(TextureUnit.Texture1);
                    GL.BindTexture(TextureTarget.Texture2D, texture.id);
                    _shader.SetInt("material.sepcular", 1);
                }
            }

            GL.BindVertexArray(this._vertexArrayObject);
            GL.DrawElements(PrimitiveType.Triangles, _indices.Length, DrawElementsType.UnsignedInt, 0); 
            // GL.BindVertexArray(0);

            //GL.ActiveTexture(TextureUnit.Texture0);
        }
    }

    public struct Vertex
    {
        public Vector3 Position;
        public Vector3 Normal;
        public Vector2 TexCoords;
    }

    public struct Texture
    {
        public int id;
        public string type;        //类型  用于 漫反射 还是高光
        public string path;        //本地图的路径
    }

}
